package com.example.smartwaterapi.filter;

import com.alibaba.fastjson2.JSON;
import com.example.smartwaterapi.common.api.RestApiResponse;
import com.example.smartwaterapi.security.JwtTokenUtil;
import com.example.smartwaterapi.security.SecurityUser;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 基于用户ID的智能限流过滤器
 * 根据用户ID和接口类型进行差异化限流
 */
@Slf4j
@Component
public class UserRateLimitFilter extends OncePerRequestFilter {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    @Lazy
    private UserDetailsService userDetailsService;
    
    @Value("${rate-limit.user.enable:true}")
    private boolean userLimitEnable;
    
    @Value("${rate-limit.user.window-seconds:60}")
    private int windowSeconds;
    
    // 不同接口类型的限流配置
    private static final Map<String, RateLimitConfig> API_LIMITS = new HashMap<>();
    
    static {
        // 注意：认证接口(auth)不在此配置，因为认证接口无法获取用户ID，
        // 由IP限流过滤器进行保护
        
        // 核心业务接口 - 正常限制
        API_LIMITS.put("water", new RateLimitConfig(300, 60, "饮水记录接口"));
        API_LIMITS.put("user", new RateLimitConfig(120, 60, "用户信息接口"));
        API_LIMITS.put("device", new RateLimitConfig(150, 60, "设备管理接口"));
        API_LIMITS.put("stats", new RateLimitConfig(100, 60, "统计分析接口"));
        
        // 查询接口 - 相对宽松
        API_LIMITS.put("query", new RateLimitConfig(200, 60, "查询接口"));
        
        // 上传接口 - 较为严格
        API_LIMITS.put("upload", new RateLimitConfig(30, 60, "文件上传接口"));
        
        // 默认限制
        API_LIMITS.put("default", new RateLimitConfig(150, 60, "默认接口"));
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        
        // 如果未启用用户限流，直接放行
        if (!userLimitEnable) {
            filterChain.doFilter(request, response);
            return;
        }
        
        String uri = request.getRequestURI();
        
        // 对静态资源、健康检查等不进行限流
        if (shouldSkipRateLimit(uri)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        // 对于认证接口，跳过用户限流（因为还没有用户ID）
        if (isAuthenticationApi(uri)) {
            log.debug("认证接口跳过用户限流: {}", uri);
            filterChain.doFilter(request, response);
            return;
        }
        
        // 获取用户ID
        Long userId = getCurrentUserId(request);
        
        // 如果无法获取用户ID（未登录或token无效），跳过用户限流
        if (userId == null) {
            log.debug("无法获取用户ID，跳过用户限流: {}", uri);
            filterChain.doFilter(request, response);
            return;
        }
        
        // 获取接口类型
        String apiType = getApiType(uri, request);
        RateLimitConfig config = API_LIMITS.getOrDefault(apiType, API_LIMITS.get("default"));
        
        // 检查是否被限流
        if (isRateLimited(userId, apiType, config)) {
            handleRateLimit(response, userId, apiType, config);
            return;
        }
        
        // 记录访问
        recordAccess(userId, apiType);
        
        // 继续处理请求
        filterChain.doFilter(request, response);
    }
    
    /**
     * 获取当前用户ID
     */
    private Long getCurrentUserId(HttpServletRequest request) {
        try {
            // 从请求头获取token
            String authHeader = request.getHeader("Authorization");
            if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
                return null;
            }
            
            String token = authHeader.substring(7);
            String username = jwtTokenUtil.getUsernameFromToken(token);
            
            if (StringUtils.hasText(username)) {
                // 加载用户详情
                var userDetails = userDetailsService.loadUserByUsername(username);
                if (userDetails instanceof SecurityUser) {
                    SecurityUser securityUser = (SecurityUser) userDetails;
                    return securityUser.getUserId();
                }
            }
        } catch (Exception e) {
            log.debug("获取用户ID失败: {}", e.getMessage());
        }
        return null;
    }
    
    /**
     * 根据URI确定接口类型
     */
    private String getApiType(String uri, HttpServletRequest request) {
        if (uri.contains("/auth/")) {
            return "auth";
        } else if (uri.contains("/water/") || uri.contains("/daily-stats/")) {
            return "water";
        } else if (uri.contains("/user/")) {
            return "user";
        } else if (uri.contains("/device/")) {
            return "device";
        } else if (uri.contains("/stats/") || uri.contains("/achievement/")) {
            return "stats";
        } else if (uri.contains("/upload/") || uri.contains("/file/")) {
            return "upload";
        } else if (request.getMethod().equals("GET")) {
            return "query";
        }
        return "default";
    }
    
    /**
     * 检查是否应该跳过限流
     */
    private boolean shouldSkipRateLimit(String uri) {
        return uri.contains("/swagger") 
                || uri.contains("/v3/api-docs") 
                || uri.contains("/doc.html")
                || uri.contains("/actuator/health")
                || uri.contains("/favicon.ico")
                || uri.contains("/static/")
                || uri.endsWith(".js")
                || uri.endsWith(".css")
                || uri.endsWith(".png")
                || uri.endsWith(".jpg")
                || uri.endsWith(".jpeg")
                || uri.endsWith(".gif")
                || uri.endsWith(".svg");
    }
    
    /**
     * 检查是否是认证接口（这些接口无法获取用户ID，跳过用户限流让用户正常登录）
     */
    private boolean isAuthenticationApi(String uri) {
        return uri.contains("/api/auth/login") 
                || uri.contains("/api/auth/register")
                || uri.contains("/api/auth/refreshToken")
                || uri.contains("/api/auth/logout")
                || uri.contains("/api/auth/")  // 通用认证接口前缀
                || uri.contains("/login")
                || uri.contains("/register");
    }
    
    /**
     * 检查是否被限流
     */
    private boolean isRateLimited(Long userId, String apiType, RateLimitConfig config) {
        String redisKey = String.format("user_rate_limit:%d:%s", userId, apiType);
        
        try {
            // 使用滑动窗口算法
            long now = System.currentTimeMillis();
            long windowStart = now - config.periodSeconds * 1000L;
            
            // 移除过期的访问记录
            redisTemplate.opsForZSet().removeRangeByScore(redisKey, 0, windowStart);
            
            // 获取当前窗口内的访问次数
            Long count = redisTemplate.opsForZSet().zCard(redisKey);
            int currentCount = (count != null) ? count.intValue() : 0;
            
            log.debug("用户 {} 的 {} 接口访问次数: {}/{}", userId, apiType, currentCount, config.maxRequests);
            
            return currentCount >= config.maxRequests;
            
        } catch (Exception e) {
            log.error("检查用户限流状态失败: {}", e.getMessage(), e);
            // 发生异常时不限流，避免影响正常业务
            return false;
        }
    }
    
    /**
     * 记录用户访问
     */
    private void recordAccess(Long userId, String apiType) {
        try {
            String redisKey = String.format("user_rate_limit:%d:%s", userId, apiType);
            long now = System.currentTimeMillis();
            
            // 使用时间戳作为分数，UUID作为值确保唯一性
            redisTemplate.opsForZSet().add(redisKey, 
                String.format("%d:%d", now, Thread.currentThread().getId()), now);
            
            // 设置过期时间，避免内存泄漏
            redisTemplate.expire(redisKey, windowSeconds * 2, TimeUnit.SECONDS);
            
        } catch (Exception e) {
            log.error("记录用户访问失败: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 处理限流响应
     */
    private void handleRateLimit(HttpServletResponse response, Long userId, 
                                String apiType, RateLimitConfig config) throws IOException {
        
        log.warn("用户 {} 访问 {} 接口被限流，限制: {}/{}秒", 
                userId, config.description, config.maxRequests, config.periodSeconds);
        
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.setStatus(429); // 429 Too Many Requests
        
        // 设置响应头，告知客户端何时可以重试
        response.setHeader("Retry-After", String.valueOf(config.periodSeconds));
        response.setHeader("X-RateLimit-Limit", String.valueOf(config.maxRequests));
        response.setHeader("X-RateLimit-Window", String.valueOf(config.periodSeconds));
        
        // 构造友好的错误响应
        Map<String, Object> errorData = new HashMap<>();
        errorData.put("retryAfter", config.periodSeconds);
        errorData.put("limitType", apiType);
        errorData.put("suggestion", getSuggestionMessage(apiType));
        
        RestApiResponse<Map<String, Object>> errorResponse = RestApiResponse.custom(
                org.springframework.http.HttpStatus.TOO_MANY_REQUESTS, 
                "429", 
                String.format("%s访问过于频繁，请 %d 秒后重试", config.description, config.periodSeconds),
                errorData);
        
        response.getWriter().write(JSON.toJSONString(errorResponse));
        response.getWriter().flush();
    }
    
    /**
     * 获取建议信息
     */
    private String getSuggestionMessage(String apiType) {
        switch (apiType) {
            case "auth":
                return "频繁登录可能存在安全风险，请检查账号安全";
            case "upload":
                return "文件上传请适度，避免频繁操作";
            case "water":
                return "饮水记录请合理添加，避免重复提交";
            default:
                return "请适度使用接口，避免频繁请求";
        }
    }
    
    /**
     * 限流配置
     */
    private static class RateLimitConfig {
        final int maxRequests;      // 最大请求数
        final int periodSeconds;    // 时间窗口（秒）
        final String description;   // 描述
        
        RateLimitConfig(int maxRequests, int periodSeconds, String description) {
            this.maxRequests = maxRequests;
            this.periodSeconds = periodSeconds;
            this.description = description;
        }
    }
}
